From 8cf6cfe286aa6949f9bd546d03e275e21e9723db Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Mon, 8 Aug 2005 10:59:22 +0000 Subject: [PATCH] Fix both Xen and XenLinux to correctly handle 64-bit time deltas. Good for robustness and future-proofing. Signed-off-by: Keir Fraser --- .../arch/xen/i386/kernel/time.c | 58 +++++++++++-------- xen/arch/x86/time.c | 52 ++++++++++++----- 2 files changed, 70 insertions(+), 40 deletions(-) diff --git a/linux-2.6-xen-sparse/arch/xen/i386/kernel/time.c b/linux-2.6-xen-sparse/arch/xen/i386/kernel/time.c index 051378a7b4..0cbdb6d5f7 100644 --- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/time.c +++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/time.c @@ -166,25 +166,34 @@ struct timer_opts timer_tsc = { .delay = delay_tsc, }; -static inline u32 down_shift(u64 time, int shift) -{ - if ( shift < 0 ) - return (u32)(time >> -shift); - return (u32)((u32)time << shift); -} - /* - * 32-bit multiplication of integer multiplicand and fractional multiplier - * yielding 32-bit integer product. + * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, + * yielding a 64-bit result. */ -static inline u32 mul_frac(u32 multiplicand, u32 multiplier) +static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift) { - u32 product_int, product_frac; + u64 product; + u32 tmp; + + if ( shift < 0 ) + delta >>= -shift; + else + delta <<= shift; + __asm__ ( - "mul %3" - : "=a" (product_frac), "=d" (product_int) - : "0" (multiplicand), "r" (multiplier) ); - return product_int; + "pushl %%edx ; " + "mull %3 ; " + "popl %%eax ; " + "pushl %%edx ; " + "mull %3 ; " + "popl %3 ; " + "addl %3,%%eax ; " + "xorl %3,%3 ; " + "adcl %3,%%edx ; " + : "=A" (product), "=r" (tmp) + : "A" (delta), "1" (mul_frac) ); + + return product; } void init_cpu_khz(void) @@ -192,27 +201,28 @@ void init_cpu_khz(void) u64 __cpu_khz = 1000000ULL << 32; struct vcpu_time_info *info = &HYPERVISOR_shared_info->vcpu_time[0]; do_div(__cpu_khz, info->tsc_to_system_mul); - cpu_khz = down_shift(__cpu_khz, -info->tsc_shift); + if ( info->tsc_shift < 0 ) + cpu_khz = __cpu_khz >> -info->tsc_shift; + else + cpu_khz = __cpu_khz << info->tsc_shift; printk(KERN_INFO "Xen reported: %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); } static u64 get_nsec_offset(struct shadow_time_info *shadow) { - u64 now; - u32 delta; + u64 now, delta; rdtscll(now); - delta = down_shift(now - shadow->tsc_timestamp, shadow->tsc_shift); - return mul_frac(delta, shadow->tsc_to_nsec_mul); + delta = now - shadow->tsc_timestamp; + return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift); } static unsigned long get_usec_offset(struct shadow_time_info *shadow) { - u64 now; - u32 delta; + u64 now, delta; rdtscll(now); - delta = down_shift(now - shadow->tsc_timestamp, shadow->tsc_shift); - return mul_frac(delta, shadow->tsc_to_usec_mul); + delta = now - shadow->tsc_timestamp; + return scale_delta(delta, shadow->tsc_to_usec_mul, shadow->tsc_shift); } static void update_wallclock(void) diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c index 0ed7a845c7..a29867e6a1 100644 --- a/xen/arch/x86/time.c +++ b/xen/arch/x86/time.c @@ -67,13 +67,6 @@ static struct time_scale platform_timer_scale; static spinlock_t platform_timer_lock = SPIN_LOCK_UNLOCKED; static u64 (*read_platform_count)(void); -static inline u32 down_shift(u64 time, int shift) -{ - if ( shift < 0 ) - return (u32)(time >> -shift); - return (u32)((u32)time << shift); -} - /* * 32-bit division of integer dividend and integer divisor yielding * 32-bit fractional quotient. @@ -83,7 +76,7 @@ static inline u32 div_frac(u32 dividend, u32 divisor) u32 quotient, remainder; ASSERT(dividend < divisor); __asm__ ( - "div %4" + "divl %4" : "=a" (quotient), "=d" (remainder) : "0" (0), "1" (dividend), "r" (divisor) ); return quotient; @@ -103,6 +96,36 @@ static inline u32 mul_frac(u32 multiplicand, u32 multiplier) return product_int; } +/* + * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, + * yielding a 64-bit result. + */ +static inline u64 scale_delta(u64 delta, struct time_scale *scale) +{ + u64 product; + u32 tmp; + + if ( scale->shift < 0 ) + delta >>= -scale->shift; + else + delta <<= scale->shift; + + __asm__ ( + "pushl %%edx ; " + "mull %3 ; " + "popl %%eax ; " + "pushl %%edx ; " + "mull %3 ; " + "popl %3 ; " + "addl %3,%%eax ; " + "xorl %3,%3 ; " + "adcl %3,%%edx ; " + : "=A" (product), "=r" (tmp) + : "A" (delta), "1" (scale->mul_frac) ); + + return product; +} + void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs) { if ( timer_ack ) @@ -486,11 +509,9 @@ static int init_cyclone(void) static s_time_t __read_platform_stime(u64 platform_time) { - u64 diff64 = platform_time - platform_timer_stamp; - u32 diff = down_shift(diff64, platform_timer_scale.shift); + u64 diff = platform_time - platform_timer_stamp; ASSERT(spin_is_locked(&platform_timer_lock)); - return (stime_platform_stamp + - (u64)mul_frac(diff, platform_timer_scale.mul_frac)); + return (stime_platform_stamp + scale_delta(diff, &platform_timer_scale)); } static s_time_t read_platform_stime(void) @@ -619,13 +640,12 @@ static unsigned long get_cmos_time(void) s_time_t get_s_time(void) { struct cpu_time *t = &cpu_time[smp_processor_id()]; - u64 tsc; - u32 delta; + u64 tsc, delta; s_time_t now; rdtscll(tsc); - delta = down_shift(tsc - t->local_tsc_stamp, t->tsc_scale.shift); - now = t->stime_local_stamp + (u64)mul_frac(delta, t->tsc_scale.mul_frac); + delta = tsc - t->local_tsc_stamp; + now = t->stime_local_stamp + scale_delta(delta, &t->tsc_scale); return now; } -- 2.30.2